import { EventClass } from "@lib/alen-helper/node/baseClass";
import { AppAisle } from "@lib/alen-helper/node/appAisle";
import { AppAisleServer } from "@lib/alen-helper/node/appAisle/appAisleServer";
import { NetServerSDrive } from "@lib/alen-helper/node/appAisle/serverDrives/net.server.drive";
import { libLogger } from "@lib/alen-helper/node/logger";
import { concatMap, map, of, Subscription, delay, first, from, tap } from "rxjs";
import { Application } from "../application/application";
import { ApplicationSDK } from "./application.sdk";
import { WindowSDK } from "./window.sdk";
import * as Fs from "fs";

libLogger.init({
    showType: ['info', 'error', 'warn'],
})

export namespace MainSDK {
    export type CmdData = Runtime.Controller.Tray.CmdData & Runtime.Controller.Window.CmdData & Runtime.Controller.ComonnCmdData;
}

export class MainSDK extends EventClass<{
    sdkReady: string,
    message: Application.Message,
    appReady: string,
}> {
    public readonly aisle: AppAisle<MainSDK.CmdData>;
    public readonly cwd: string;
    private readonly ipcServer: AppAisleServer<AppAisle.Cmds>;
    private _connSubs: Subscription;
    constructor(
        private readonly _drive: AppAisle.Drive,
        options: Partial<AppAisle.Options>,
        BaseOptions?: Partial<EventClass.BaseOptions>
    ) {
        super('MainSDK', BaseOptions);
        this.cwd = process.cwd();
        this.aisle = this._newClass(AppAisle, this._drive, options);
        this.aisle.setHandler(this._aisleHandler);
        this.ipcServer = this._newClass(AppAisleServer, this._newClass(NetServerSDrive, {}));
        this.ipcServer.setHandler(this._ipcHandler);
        this._sub();
        this._connSubs = this.aisle.conn()
            .subscribe(() => {
                //...
            });
    }

    private _aisleHandler: Partial<AppAisle.CmdHandlers<Runtime.Controller.ComonnCmdData_Client>> = {
        'app_activate_id': (data, aisle) => {
            this._appid = data.data.param.appid;
            this._ipcChannel = data.data.param.ipcChannel;
            this._cacheDir = data.data.param.cacheDir;
            return this._initActivate()
                .pipe(map(() => {
                    this._isSdkReady();
                    return { appid: data.data.param.appid }
                }));
        },
        'app_event_message': (data, aisle) => {
            return this.triggerEventEnd('message', data.data.param, null, 'eventData').pipe(map(e => {
                return !e.isError;
            }));
        },
    }

    private _getWinItem(aisle: AppAisle | string) {
        let item = typeof aisle === 'string' ? this.window.getItem(aisle) : this.window.getItem_byIpcAisle(aisle);
        if (!item) throw this._err('项目不可用', '找不到对应的窗口');
        return item;
    }

    private _ipcHandler: Partial<AppAisle.CmdHandlers<Runtime.PreloadCmdData>> = {
        'win_activate': (data, aisle) => {
            this.window.itemIpcActivate(data.data.param.id, aisle);
            return {
                appid: this.appid!,
                cwd: this.cwd,
            }
        },
        'win_ready': (data, aisle) => {
            const win = this._getWinItem(aisle);
            this.window.itemIpcReady(win.id);
            return true;
        },
        'win_close': (data, aisle) => {
            const win = this._getWinItem(data.data.param.id ? data.data.param.id : aisle);
            return win.close();
        },
        'win_message': (data, aisle) => {
            const win = this._getWinItem(aisle);
            return this.message(data.data.param.data, { target: data.data.param.target, windowId: win.id });
        },
    }

    public readonly winIpcChannels: { [id: string]: AppAisle<Runtime.PreloadCmdData> } = {};

    private _isSdkReady() {
        if (!this.appid || !this._isActivate) return;
        this._log.log(`sdkReady appid: ${this.appid}`);
        this._sdkReady = true;
        this.triggerEvent('sdkReady', this.appid);
    }

    private _sdkReady = false;
    private _isActivate: boolean = false;
    private _appid: string | null = null;
    private _ipcChannel: string | null = null;
    private _cacheDir: string | null = null;

    get appid() {
        return this._appid;
    }

    private _initActivate() {
        return from(Fs.promises.mkdir(this._cacheDir!, { recursive: true }))
            .pipe(concatMap(() => {
                this._log.info(`缓存文件夹:${this._cacheDir!}`);
                return this.ipcServer.listen(this._ipcChannel!);
            }))
            .pipe(tap(() => {
                this._log.info(`IPC通道:${this._ipcChannel!}`);
            }));

    }

    private _subs: Subscription | null = null;

    private _unSub() {
        if (this._subs) this._subs.unsubscribe();
        this._subs = null;
    }

    private _sub() {
        this._unSub();
        this._subs = this.aisle.event('activate')
            .subscribe(data => {
                this._isActivate = true;
                this._isSdkReady();
            });
        this._subs.add(
            this.ipcServer.event('listened')
                .subscribe(() => {
                    //this._log.info(`IPC:${this.ipcServer.address()}`);
                })
        );

        this.addEventHandler('message', e => {
            if (!e.data.target.id) return null;
            const win = this._getWinItem(e.data.target.id);
            if (!win.ipcAisle) throw this._err('操作失败');
            return win.ipcAisle.cmd('win_event_message', e.data)
                .pipe(map(back => {
                    if (!back) throw this._err('操作失败');
                    return null;
                }));
        });
    }

    private _modules: {
        application: ApplicationSDK,
        window: WindowSDK,

    } | null = null;

    private _setModules() {
        if (!this.appid) throw this._err('未初始化', '没有APP ID');
        const application = this._newClass(ApplicationSDK, {
            appid: this.appid,
            aisle: this.aisle,
        });
        const window = this._newClass(WindowSDK, {
            appid: this.appid,
            aisle: this.aisle,
        });
        this._modules = { application, window };
        this._modules.application.init();
        this._modules.window.init();
    }

    get application() {
        if (!this._modules) throw this._err('未初始化');
        return this._modules.application;
    }

    get window() {
        if (!this._modules) throw this._err('未初始化');
        return this._modules.window;
    }

    message(data: Application.Message['data'], options: { target?: Partial<Application.Message['target']>, windowId?: string | null, } = {}) {
        const windowId = options.windowId ? options.windowId : null;
        const target: Application.Message['target'] = Object.assign({ appid: this.appid!, id: null }, options.target ? options.target : {});
        //return this.aisle.cmd('app_message', { windowId, appid: target.appid, id: target.id, data, });
        if (target.appid === this.appid) {
            return this.triggerEventEnd('message', {
                source: { appid: this.appid, id: windowId },
                target,
                data,
            }, null, 'eventData').pipe(map(e => {
                return !e.isError;
            }));
        } else {
            return this.aisle.cmd('app_message', { windowId, appid: target.appid, id: target.id, data, });
        }
    }

    appReady() {
        return of(this._sdkReady)
            .pipe(concatMap(a => {
                if (a) return of(null);
                return this.event('sdkReady')
            }))
            .pipe(first())
            .pipe(concatMap(() => {
                this._setModules();
                return this.aisle.cmd('app_ready', { appid: this._appid! });
            }))
            .pipe(map(data => {
                this.triggerEvent('appReady', this._appid!);
                this._log.log(`appReady appid: ${this._appid!}`);
                return this._appid!;
            }));
    }

}